A “Java Virtual Machine” (JVM) (also referred to as a “Java Runtime”) is a program that can execute other programs (“application programs”) provided as Java bytecode. Java bytecode usually, but not exclusively, is generated from programs written in the Java programming language. Java program code may be executed in an interpreted mode, in compiled mode, or using a mixture of the two modes. In addition to governing the execution of an application's bytecodes, the JVM handles related tasks such as managing memory, providing security, and managing multiple threads of program execution. The JVM is a so-called “layer” on top of the operating system of the host (i.e., the physical machine). The JVM thus provides portability of applications across various operating systems and corresponding physical machines (i.e., hardware).
A “dynamic compiler” refers to a compiler built into a virtual machine, such as a JVM. For example, a dynamic compiler may compile procedures of the target program immediately before their first invocation or concurrently with execution. Dynamic compilers may provide accelerated execution of Java bytecode in JVMs. Moreover, a virtual machine with a dynamic compiler may execute a program code in an interpreted mode, in a dynamically compiled mode, or a combination of the two modes. A dynamic compiler may generate optimized code sequences (corresponding to the code sequences of an application) as the application is being executed by an interpreter. The generated code sequence may be stored in cache, and subsequent invocations of the same code sequence prompt an interpreter to query cache for the existence of a compiled version of the same code. If such a compiled version exists, then it is executed, else the incoming code sequence is interpreted by the interpreter. Concurrent with the execution of a procedure, the compiler may compile or recompile a procedure several times to further optimize it. The dynamic compiler is considered “dynamic” since it generates optimized code for applications while these applications are running, and the dynamic compiler may generate new, more optimized code, while the original code is being executed, interpreted, or compiled. In contrast, a “static compiler” completely compiles the program (e.g., into machine specific binary code or into portable bytecode) before it is executed, and compilation and optimization are not performed dynamically as the application is being executed.
To “debug” a program refers to inspection and manipulation of the operations of the program while the program is being executed (e.g., by a virtual machine). This may include setting breakpoints, single stepping, observing the program's variables, changing the program state (e.g., the state of the virtual machine), and registering for events that might occur during execution.
Any compiler, static, or dynamic, needs to know in advance whether it must produce code that can be debugged (“is debuggable”) or not. Producing debuggable code restricts the compiler in the amount of optimization that may be applied to the original program. A larger degree of debuggability means a lesser degree of possible optimization, and vice versa. While it may be the case that the performance of a debuggable program need not be optimized (e.g. since debuggability might play a role only during development), enhanced supportability of running server application programs may be needed to balance the achievable performance of the application programs with the desired degree of debuggability.
A “live variable” refers to a local variable that is being used in the current execution path of a program. Specifically, a local variable X is called “live” (or “live out”) at some point P in a program, if there is a path in the program from that point P to another point Q, such that the value of the variable X is read at point Q but X does not get written to (i.e., overwritten) on this path; otherwise, the variable X is considered to be a “dead variable” at point P in the program. A dead variable cannot have any impact on the future behavior of the program. The only reason dead variables may have to be maintained in a compiled program (e.g. by saving their values from processor registers to memory) is for extended debugging purposes.
The Java Virtual Machine Tools Interface (JVMTI) is an application programming interface (API) that allows a program, called the “JVMTI client,” to inspect the state and to control the execution of application programs running in a JVM, in this context referred to as a “JVMTI server.” As such, JVMTI is used to implement debuggers, profilers, and other tools accessing the state of the JVM and the application programs running within the JVM. Since the JVMTI is a native interface of the JVM, a JVMTI client is implemented as a library that is loaded during JVM initialization. When loaded, the JVMTI client may gain access to and manipulate the JVMTI server (i.e., the JVM) via procedure calls (also referred to as “in-process messages”). These procedure calls may be directed from the JVMTI client to the JVMTI server, but may also occur in the other direction.
The Java Debug Wire Protocol (JDWP) is a communication protocol between a JDWP client and a JDWP server that is associated with a JVM. A “protocol” generally refers to allowed exchanges of messages between two communicating parties. The part that initiates the communication is called the “client,” while the so-called “server” operates on behalf of the client. In particular, a “debugging protocol” specifies the way a debugger client may communicate with a debug server, such as a JVM. A JDWP client typically includes an interactively used debugger that allows the user to debug a running Java program in the remote JVM associated with the JDWP server. The message exchange between the JDWP client and the JDWP server typically takes place across a network, as opposed to the in-process message exchange in JVMTI. The JDWP server typically is implemented as a JVMTI client hosted by a JVMTI-capable JVM.